home *** CD-ROM | disk | FTP | other *** search
/ MacFormat 1995 January / macformat-020.iso / Shareware City / Developers / OutOfPhase1.01Source / OutOfPhase Folder / ExecuteSynthesis.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-10-01  |  17.7 KB  |  506 lines  |  [TEXT/KAHL]

  1. /* ExecuteSynthesis.c */
  2. /*****************************************************************************/
  3. /*                                                                           */
  4. /*    Out Of Phase:  Digital Music Synthesis on General Purpose Computers    */
  5. /*    Copyright (C) 1994  Thomas R. Lawrence                                 */
  6. /*                                                                           */
  7. /*    This program is free software; you can redistribute it and/or modify   */
  8. /*    it under the terms of the GNU General Public License as published by   */
  9. /*    the Free Software Foundation; either version 2 of the License, or      */
  10. /*    (at your option) any later version.                                    */
  11. /*                                                                           */
  12. /*    This program is distributed in the hope that it will be useful,        */
  13. /*    but WITHOUT ANY WARRANTY; without even the implied warranty of         */
  14. /*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          */
  15. /*    GNU General Public License for more details.                           */
  16. /*                                                                           */
  17. /*    You should have received a copy of the GNU General Public License      */
  18. /*    along with this program; if not, write to the Free Software            */
  19. /*    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.              */
  20. /*                                                                           */
  21. /*    Thomas R. Lawrence can be reached at tomlaw@world.std.com.             */
  22. /*                                                                           */
  23. /*****************************************************************************/
  24.  
  25. #include "MiscInfo.h"
  26. #include "Audit.h"
  27. #include "Debug.h"
  28. #include "Definitions.h"
  29.  
  30. #include "ExecuteSynthesis.h"
  31. #include "Memory.h"
  32. #include "MainWindowStuff.h"
  33. #include "LinearTransition.h"
  34. #include "PlayTrackInfoThang.h"
  35. #include "Array.h"
  36. #include "WaveTableOscControl.h"
  37. #include "EnvelopeState.h"
  38. #include "LFOGenerator.h"
  39. #include "InstrList.h"
  40. #include "InstrObject.h"
  41. #include "TrackObject.h"
  42. #include "SampleOscControl.h"
  43. #include "ModulationOscControl.h"
  44. #include "Fractions.h"
  45. #include "FrameObject.h"
  46. #include "DeterminedNoteStructure.h"
  47. #include "TempoController.h"
  48. #include "OscBankPlayer.h"
  49. #include "DataMunging.h"
  50. #include "Alert.h"
  51. #include "CheckNameUniqueness.h"
  52. #include "ErrorDaemon.h"
  53.  
  54.  
  55. typedef struct PlayListNodeRec
  56.     {
  57.         struct PlayListNodeRec*    Next;
  58.         PlayTrackInfoRec*                ThisTrack;
  59.     } PlayListNodeRec;
  60.  
  61.  
  62. /* build the list of objects involved in playing. */
  63. static SynthErrorCodes    BuildPlayList(PlayListNodeRec** ListOut,
  64.                                                 ArrayRec* TrackObjectList, MyBoolean InStereoFlag,
  65.                                                 MainWindowRec* MainWindow,
  66.                                                 LargeBCDType OverallVolumeScalingReciprocal,
  67.                                                 long SamplingRate, long EnvelopeRate, MyBoolean TimeInterp,
  68.                                                 MyBoolean WaveInterp, TempoControlRec* TempoControl,
  69.                                                 long ScanningGapWidthInEnvelopeTicks,
  70.                                                 ErrorDaemonRec* ErrorDaemon)
  71.     {
  72.         PlayListNodeRec*        TrackPlayList;
  73.         long                                Scan;
  74.         long                                Limit;
  75.         InstrListRec*                InstrList;
  76.         SynthErrorCodes            Error;
  77.  
  78.         /* build list of tracks */
  79.         CheckPtrExistence(MainWindow);
  80.         CheckPtrExistence(TrackObjectList);
  81.         InstrList = MainWindowGetInstrList(MainWindow);
  82.         TrackPlayList = NIL;
  83.         Limit = ArrayGetLength(TrackObjectList);
  84.         for (Scan = 0; Scan < Limit; Scan += 1)
  85.             {
  86.                 TrackObjectRec*            PossibleTrack;
  87.                 PlayListNodeRec*        NewListNode;
  88.                 PlayTrackInfoRec*        TrackPlayObject;
  89.                 InstrObjectRec*            BaseInstrument;
  90.                 char*                                InstrumentNameCopy;
  91.  
  92.                 /* get the track */
  93.                 PossibleTrack = (TrackObjectRec*)ArrayGetElement(TrackObjectList,Scan);
  94.                 CheckPtrExistence(PossibleTrack);
  95.                 /* get the instrument to be used */
  96.                 InstrumentNameCopy = TrackObjectGetInstrName(PossibleTrack);
  97.                 if (InstrumentNameCopy == NIL)
  98.                     {
  99.                         Error = eSynthNoMemory;
  100.                         goto TrackListBuildFailure1;
  101.                     }
  102.                 BaseInstrument = InstrListLookupNamedInstr(InstrList,InstrumentNameCopy);
  103.                 ReleasePtr(InstrumentNameCopy);
  104.                 if (BaseInstrument == NIL)
  105.                     {
  106.                         char*                                TrackName;
  107.                         MyBoolean                        MessageSuccess = False;
  108.  
  109.                         TrackName = TrackObjectGetNameCopy(PossibleTrack);
  110.                         if (TrackName != NIL)
  111.                             {
  112.                                 char*                                NullTerminated;
  113.  
  114.                                 NullTerminated = BlockToStringCopy(TrackName);
  115.                                 if (NullTerminated != NIL)
  116.                                     {
  117.                                         AlertHalt("The track '_' uses a nonexistent instrument.",
  118.                                             NullTerminated);
  119.                                         MessageSuccess = True;
  120.                                         ReleasePtr(NullTerminated);
  121.                                     }
  122.                                 ReleasePtr(TrackName);
  123.                             }
  124.                         if (!MessageSuccess)
  125.                             {
  126.                                 AlertHalt("A track uses a nonexistent instrument.",NIL);
  127.                             }
  128.                         Error = eSynthUndefinedInstrumentError;
  129.                         goto TrackListBuildFailure1;
  130.                     }
  131.                 /* build the track */
  132.                 TrackPlayObject = NewPlayTrackInfo(PossibleTrack,
  133.                     GetInstrObjectRawData(BaseInstrument),InStereoFlag,
  134.                     OverallVolumeScalingReciprocal,SamplingRate,EnvelopeRate,TimeInterp,
  135.                     WaveInterp,TempoControl,ScanningGapWidthInEnvelopeTicks,ErrorDaemon);
  136.                 if (TrackPlayObject == NIL)
  137.                     {
  138.                         Error = eSynthNoMemory;
  139.                      TrackListBuildFailure1:
  140.                         while (TrackPlayList != NIL)
  141.                             {
  142.                                 PlayListNodeRec*        Temp;
  143.  
  144.                                 DisposePlayTrackInfo(TrackPlayList->ThisTrack);
  145.                                 Temp = TrackPlayList;
  146.                                 TrackPlayList = TrackPlayList->Next;
  147.                                 ReleasePtr((char*)Temp);
  148.                             }
  149.                         return Error;
  150.                     }
  151.                 /* add this one to the list */
  152.                 NewListNode = (PlayListNodeRec*)AllocPtrCanFail(sizeof(PlayListNodeRec),
  153.                     "PlayListNodeRec");
  154.                 if (NewListNode == NIL)
  155.                     {
  156.                         Error = eSynthNoMemory;
  157.                      TrackListBuildFailure2:
  158.                         DisposePlayTrackInfo(TrackPlayObject);
  159.                         goto TrackListBuildFailure1;
  160.                     }
  161.                 NewListNode->ThisTrack = TrackPlayObject;
  162.                 NewListNode->Next = TrackPlayList;
  163.                 TrackPlayList = NewListNode;
  164.             }
  165.         *ListOut = TrackPlayList;
  166.         return eSynthDone;
  167.     }
  168.  
  169.  
  170. /* this routine scans through the key playlist and determines the exact point */
  171. /* at which playback should begin. */
  172. static void                        FindStartPoint(TrackObjectRec* KeyTrack, long FrameToStartAt,
  173.                                                 FractionRec* StartTimeOut)
  174.     {
  175.         long                                Scan;
  176.         FractionRec                    Counter;
  177.  
  178.         CheckPtrExistence(KeyTrack);
  179.         Counter.Integer = 0;
  180.         Counter.Fraction = 0;
  181.         Counter.Denominator = (64*3*5*7*2);
  182.         ERROR(FrameToStartAt > TrackObjectGetNumFrames(KeyTrack),PRERR(ForceAbort,
  183.             "FindStartPoint:  start frame is beyond end of track"));
  184.         for (Scan = 0; Scan < FrameToStartAt; Scan += 1)
  185.             {
  186.                 FrameObjectRec*            Frame;
  187.                 FractionRec                    TempDuration;
  188.  
  189.                 Frame = TrackObjectGetFrame(KeyTrack,Scan);
  190.                 CheckPtrExistence(Frame);
  191.                 DurationOfFrame(Frame,&TempDuration);
  192.                 AddFractions(&TempDuration,&Counter,&Counter);
  193.             }
  194.         *StartTimeOut = Counter;
  195.     }
  196.  
  197.  
  198. /* This routine does all of the work. */
  199. /* The DataOutCallback is called every time a block of data is */
  200. /* ready to be sent to the target device; this is provided so that data can be */
  201. /* redirected to a file or postprocessed in some way before playback. */
  202. /* the KeyTrack and FrameToStartAt provide a reference point indicating where */
  203. /* playback should occur.  if KeyTrack is NIL, then playback begins at the beginning. */
  204. /* the rate parameters are in operations per second. */
  205. SynthErrorCodes                Synthesizer(struct MainWindowRec* MainWindow,
  206.                                                 MyBoolean (*DataOutCallback)(void* Refcon,
  207.                                                     largefixedsigned* DataBlock, long NumFrames,
  208.                                                     MyBoolean* AbortPlaybackFlagOut),
  209.                                                 void* DataOutRefcon, struct ArrayRec* ListOfTracks,
  210.                                                 struct TrackObjectRec* KeyTrack, long FrameToStartAt,
  211.                                                 long SamplingRate, long EnvelopeRate, MyBoolean UseStereo,
  212.                                                 LargeBCDType DefaultBeatsPerMinute,
  213.                                                 LargeBCDType OverallVolumeScalingReciprocal,
  214.                                                 MyBoolean InterpOverTime, MyBoolean InterpAcrossWaves,
  215.                                                 LargeBCDType ScanningGap, ErrorDaemonRec* ErrorDaemon)
  216.     {
  217.         struct ArrayRec*        TrackScheduleArray;
  218.         SynthErrorCodes            Error;
  219.         largefixedsigned*        SampleArray;
  220.         long                                SampleArrayLength;
  221.  
  222.  
  223.         /* error checking */
  224.         CheckPtrExistence(MainWindow);
  225.         CheckPtrExistence(ListOfTracks);
  226.         CheckPtrExistence(ErrorDaemon);
  227.         ERROR(DataOutCallback == NIL,PRERR(ForceAbort,"Synthesizer: bad DataOutCallback"));
  228.  
  229.         /* check to see that there aren't any naming ambiguities */
  230.         if (!CheckNameUniqueness(MainWindow))
  231.             {
  232.                 return eSynthDuplicateNames;
  233.             }
  234.  
  235.         /* make sure all objects are up to date */
  236.         if (MainWindowMakeEverythingUpToDate(MainWindow))
  237.             {
  238.                 PlayListNodeRec*        PlayTrackList;
  239.  
  240.                 SampleArrayLength = 1000;
  241.                 SampleArray = (largefixedsigned*)AllocPtrCanFail(SampleArrayLength
  242.                     * sizeof(largefixedsigned),"SampleArray");
  243.                 if (SampleArray != NIL)
  244.                     {
  245.                         TempoControlRec*        TempoControl;
  246.                         long                                ScanningGapWidthInEnvelopeTicks;
  247.  
  248.                         /* figure out how large the scanning gap is */
  249.                         ScanningGapWidthInEnvelopeTicks = LargeBCD2Double(ScanningGap) * EnvelopeRate;
  250.  
  251.                         TempoControl = NewTempoControl(DefaultBeatsPerMinute);
  252.                         if (TempoControl != NIL)
  253.                             {
  254.                                 Error = BuildPlayList(&PlayTrackList,ListOfTracks,UseStereo,MainWindow,
  255.                                     OverallVolumeScalingReciprocal,SamplingRate,EnvelopeRate,
  256.                                     InterpOverTime,InterpAcrossWaves,TempoControl,
  257.                                     ScanningGapWidthInEnvelopeTicks,ErrorDaemon);
  258.                                 if (Error == eSynthDone)
  259.                                     {
  260.                                         PlayListNodeRec*        Scan;
  261.                                         PlayListNodeRec*        Lag;
  262.                                         MyBoolean                        AbortPlaybackFlag;
  263.                                         FractionRec                    MomentOfStarting;
  264.                                         LargeBCDType                CurrentBeatsPerMinute;
  265.                                         long                                ScanningGapFrontInEnvelopeTicks;
  266.                                         double                            EnvelopeClockAccumulatorFraction;
  267.                                         double                            NoteDurationClockAccumulatorFraction;
  268.                                         double                            SamplesPerEnvelopeClock;
  269.                                         double                            DurationTicksPerEnvelopeClock;
  270.  
  271.                                         /* calculate the moment of starting for tracks */
  272.                                         FindStartPoint(KeyTrack,FrameToStartAt,&MomentOfStarting);
  273.                                         /* cue all tracks up to this point */
  274.                                         Scan = PlayTrackList;
  275.                                         while (Scan != NIL)
  276.                                             {
  277.                                                 if (!CuePlayTrackInfoToPoint(Scan->ThisTrack,&MomentOfStarting))
  278.                                                     {
  279.                                                         Error = eSynthNoMemory;
  280.                                                         goto PlaybackFailurePoint;
  281.                                                     }
  282.                                                 Scan = Scan->Next;
  283.                                             }
  284.  
  285.                                         /* this value is for determining when in REAL time (not score time) */
  286.                                         /* each note begins */
  287.                                         ScanningGapFrontInEnvelopeTicks = 0;
  288.  
  289.                                         /* initialize tempo thing */
  290.                                         CurrentBeatsPerMinute = DefaultBeatsPerMinute;
  291.  
  292.                                         /* initialize accumulators */
  293.                                         EnvelopeClockAccumulatorFraction = 0;
  294.                                         NoteDurationClockAccumulatorFraction = 0;
  295.                                         /* calculate increment factors */
  296.                                         SamplesPerEnvelopeClock = (double)SamplingRate / EnvelopeRate;
  297.                                         DurationTicksPerEnvelopeClock = ((LargeBCD2Double(DefaultBeatsPerMinute)
  298.                                             / (4/*beats per whole note*/ * 60/*seconds per minute*/))
  299.                                             / EnvelopeRate) * DURATIONUPDATECLOCKRESOLUTION;
  300.  
  301.                                         /* play */
  302.                                         AbortPlaybackFlag = False;
  303.                                         while ((PlayTrackList != NIL) && !AbortPlaybackFlag)
  304.                                             {
  305.                                                 long                                NumSampleFrames;
  306.                                                 long                                NumNoteDurationTicks;
  307.                                                 LargeBCDType                OldBeatsPerMinute;
  308.  
  309.                                                 /* figure out how many note duration ticks to generate */
  310.                                                 /* increment counter */
  311.                                                 NoteDurationClockAccumulatorFraction += DurationTicksPerEnvelopeClock;
  312.                                                 /* round down */
  313.                                                 NumNoteDurationTicks = (long)NoteDurationClockAccumulatorFraction;
  314.                                                 /* subtract off what we're taking out this time around, */
  315.                                                 /* leaving the extra little bit in there */
  316.                                                 NoteDurationClockAccumulatorFraction -= NumNoteDurationTicks;
  317.  
  318.                                                 /* figure out how many samples to do before the next envelope */
  319.                                                 if (ScanningGapFrontInEnvelopeTicks >= ScanningGapWidthInEnvelopeTicks)
  320.                                                     {
  321.                                                         /* we're really sampling stuff */
  322.                                                         EnvelopeClockAccumulatorFraction += SamplesPerEnvelopeClock;
  323.                                                         NumSampleFrames = (long)EnvelopeClockAccumulatorFraction;
  324.                                                         EnvelopeClockAccumulatorFraction -= NumSampleFrames;
  325.                                                     }
  326.                                                  else
  327.                                                     {
  328.                                                         /* scanning gap is still opening, so we're not sampling */
  329.                                                         NumSampleFrames = 0;
  330.                                                     }
  331.  
  332.                                                 /* see if we need to resize the output workspace array */
  333.                                                 if (UseStereo)
  334.                                                     {
  335.                                                         long                                Scan;
  336.  
  337.                                                         /* stereo array sizing */
  338.                                                         if (NumSampleFrames * 2 > SampleArrayLength)
  339.                                                             {
  340.                                                                 largefixedsigned*        TempArray;
  341.  
  342.                                                                 TempArray = (largefixedsigned*)ResizePtr(
  343.                                                                     (char*)SampleArray,NumSampleFrames * 2
  344.                                                                     * sizeof(largefixedsigned));
  345.                                                                 if (TempArray == NIL)
  346.                                                                     {
  347.                                                                         Error = eSynthNoMemory;
  348.                                                                         goto PlaybackFailurePoint;
  349.                                                                     }
  350.                                                                 SampleArray = TempArray;
  351.                                                                 SampleArrayLength = NumSampleFrames * 2;
  352.                                                             }
  353.                                                         /* initialize the array */
  354.                                                         for (Scan = 0; Scan < NumSampleFrames * 2; Scan += 1)
  355.                                                             {
  356.                                                                 PRNGCHK(SampleArray,&(SampleArray[Scan]),
  357.                                                                     sizeof(SampleArray[Scan]));
  358.                                                                 SampleArray[Scan] = 0;
  359.                                                             }
  360.                                                     }
  361.                                                  else
  362.                                                     {
  363.                                                         long                                Scan;
  364.  
  365.                                                         /* mono array sizing */
  366.                                                         if (NumSampleFrames > SampleArrayLength)
  367.                                                             {
  368.                                                                 largefixedsigned*        TempArray;
  369.  
  370.                                                                 TempArray = (largefixedsigned*)ResizePtr(
  371.                                                                     (char*)SampleArray,NumSampleFrames
  372.                                                                     * sizeof(largefixedsigned));
  373.                                                                 if (TempArray == NIL)
  374.                                                                     {
  375.                                                                         Error = eSynthNoMemory;
  376.                                                                         goto PlaybackFailurePoint;
  377.                                                                     }
  378.                                                                 SampleArray = TempArray;
  379.                                                                 SampleArrayLength = NumSampleFrames;
  380.                                                             }
  381.                                                         /* initialize the array */
  382.                                                         for (Scan = 0; Scan < NumSampleFrames; Scan += 1)
  383.                                                             {
  384.                                                                 PRNGCHK(SampleArray,&(SampleArray[Scan]),
  385.                                                                     sizeof(SampleArray[Scan]));
  386.                                                                 SampleArray[Scan] = 0;
  387.                                                             }
  388.                                                     }
  389.  
  390.                                                 /* perform execution cyle */
  391.                                                 Scan = PlayTrackList;
  392.                                                 Lag = NIL;
  393.                                                 while (Scan != NIL)
  394.                                                     {
  395.                                                         if (!PlayTrackUpdate(Scan->ThisTrack,
  396.                                                             /* this condition is responsible for opening the scanning gap */
  397.                                                             (ScanningGapFrontInEnvelopeTicks >= ScanningGapWidthInEnvelopeTicks),
  398.                                                             NumNoteDurationTicks,NumSampleFrames,SampleArray,
  399.                                                             /* envelope ticks per duration tick */
  400.                                                             (float)(1 / DurationTicksPerEnvelopeClock),
  401.                                                             ScanningGapFrontInEnvelopeTicks))
  402.                                                             {
  403.                                                                 Error = eSynthNoMemory;
  404.                                                                 goto PlaybackFailurePoint;
  405.                                                             }
  406.                                                         if (PlayTrackIsItStillActive(Scan->ThisTrack))
  407.                                                             {
  408.                                                                 Lag = Scan;
  409.                                                                 Scan = Scan->Next;
  410.                                                             }
  411.                                                          else
  412.                                                             {
  413.                                                                 PlayListNodeRec*        Temp;
  414.  
  415.                                                                 if (Lag == NIL)
  416.                                                                     {
  417.                                                                         PlayTrackList = Scan->Next;
  418.                                                                     }
  419.                                                                  else
  420.                                                                     {
  421.                                                                         Lag->Next = Scan->Next;
  422.                                                                     }
  423.                                                                 Temp = Scan;
  424.                                                                 Scan = Scan->Next;
  425.                                                                 DisposePlayTrackInfo(Temp->ThisTrack);
  426.                                                                 ReleasePtr((char*)Temp);
  427.                                                             }
  428.                                                     }
  429.  
  430.                                                 /* keep track of what time it is */
  431.                                                 ScanningGapFrontInEnvelopeTicks += 1;
  432.  
  433.                                                 /* submit the data */
  434.                                                 AbortPlaybackFlag = False;
  435.                                                 if (!(*DataOutCallback)(DataOutRefcon,SampleArray,NumSampleFrames,
  436.                                                     &AbortPlaybackFlag)) /* DataOutRefcon knows about stereo status */
  437.                                                     {
  438.                                                         Error = eSynthDataSubmitError;
  439.                                                         goto PlaybackFailurePoint;
  440.                                                     }
  441.  
  442.                                                 /* and also do the tempo generator */
  443.                                                 OldBeatsPerMinute = CurrentBeatsPerMinute;
  444.                                                 CurrentBeatsPerMinute = TempoControlUpdate(TempoControl,
  445.                                                     NumNoteDurationTicks);
  446.                                                 if (OldBeatsPerMinute != CurrentBeatsPerMinute)
  447.                                                     {
  448.                                                         /* tempo changed, so we have to adjust the clock */
  449.                                                         DurationTicksPerEnvelopeClock = ((LargeBCD2Double(CurrentBeatsPerMinute)
  450.                                                             / (4/*beats per whole note*/ * 60/*seconds per minute*/))
  451.                                                             / EnvelopeRate) * DURATIONUPDATECLOCKRESOLUTION;
  452.                                                     }
  453.                                             }
  454.                                         if (AbortPlaybackFlag)
  455.                                             {
  456.                                                 Error = eSynthUserCancelled;
  457.                                             }
  458.  
  459.                                      PlaybackFailurePoint:
  460.                                         ;
  461.  
  462.                                         /* clean up */
  463.                                         while (PlayTrackList != NIL)
  464.                                             {
  465.                                                 PlayListNodeRec*        Temp;
  466.  
  467.                                                 DisposePlayTrackInfo(PlayTrackList->ThisTrack);
  468.                                                 Temp = PlayTrackList;
  469.                                                 PlayTrackList = PlayTrackList->Next;
  470.                                                 ReleasePtr((char*)Temp);
  471.                                             }
  472.                                     }
  473.  
  474.                                 DisposeTempoControl(TempoControl);
  475.                             }
  476.  
  477.                         /* clean up */
  478.                         ReleasePtr((char*)SampleArray);
  479.                     }
  480.                  else
  481.                     {
  482.                         Error = eSynthNoMemory;
  483.                     }
  484.  
  485.              SkipBuildLoop:
  486.                 ;
  487.             }
  488.          else
  489.             {
  490.                 Error = eSynthPrereqError;
  491.             }
  492.  
  493.         /* clean up */
  494.         FlushLinearTransitionRecords();
  495.         FlushWaveTableOscControl();
  496.         FlushSampleOscControl();
  497.         FlushEvalEnvelopeStateRecords();
  498.         FlushLFOGeneratorRecords();
  499.         FlushModOscControl();
  500.         FlushPlayTrackInfo();
  501.         FlushFrozenNoteStructures();
  502.         FlushCachedOscStateBankRecords();
  503.  
  504.         return Error;
  505.     }
  506.